/*
 * Copyright 2023, Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 */

#include <KernelExport.h>
#include <PCI.h>
#include <OS.h>
#include <graphic_driver.h>
#include <malloc.h>
#include <string.h>
#include <stdio.h>

#include "device.h"

#define TRACE_IOCTL
#ifdef TRACE_IOCTL
#define TRACE(x...) dprintf(DRIVER_PREFIX ": " x)
#else
#define TRACE(x...) ;
#endif

#define ERROR(x...) dprintf(DRIVER_PREFIX ": " x)

// 设置光标位置
static status_t
set_cursor_position(device_info *di, ls7a2000_cursor_position *pos)
{
    ls7a2000_shared_info *si = di->si;

    TRACE("set_cursor_position(): %d, %d\n", pos->x, pos->y);

    // 保存光标位置
    si->cursor_x = pos->x;
    si->cursor_y = pos->y;

    // 更新硬件光标位置
    if (si->hardware_cursor && si->cursor_visible)
    {
        WREG32(LS7A_DC_CURSOR_POS, (si->cursor_y << 16) | si->cursor_x);
    }

    return B_OK;
}

// 设置光标形状
static status_t
set_cursor_shape(device_info *di, ls7a2000_cursor_shape *shape)
{
    ls7a2000_shared_info *si = di->si;

    TRACE("set_cursor_shape(): %dx%d, hot(%d,%d)\n",
          shape->width, shape->height, shape->hot_x, shape->hot_y);

    // 检查参数
    if (shape->width > 64 || shape->height > 64)
    {
        ERROR("Cursor size too large: %dx%d (max 64x64)\n",
              shape->width, shape->height);
        return B_BAD_VALUE;
    }

    // 保存光标参数
    si->cursor_width = shape->width;
    si->cursor_height = shape->height;
    si->cursor_hot_x = shape->hot_x;
    si->cursor_hot_y = shape->hot_y;

    // 复制光标数据
    if (shape->data != NULL)
    {
        memcpy(si->cursor_buffer, shape->data, shape->width * shape->height * 4);
    }

    // 更新硬件光标
    if (si->hardware_cursor)
    {
        // 设置光标地址
        WREG32(LS7A_DC_CURSOR_ADDR, (uint32)si->physical_cursor_buffer);
    }

    return B_OK;
}

// 显示光标
static status_t
show_cursor(device_info *di, bool show)
{
    ls7a2000_shared_info *si = di->si;
    uint32 config;

    TRACE("show_cursor(): %s\n", show ? "true" : "false");

    // 保存当前配置
    config = RREG32(LS7A_DC_CONFIG);

    if (show)
    {
        // 启用硬件光标
        config |= LS7A_DC_CONFIG_CURSOR;
    }
    else
    {
        // 禁用硬件光标
        config &= ~LS7A_DC_CONFIG_CURSOR;
    }

    // 更新配置寄存器
    WREG32(LS7A_DC_CONFIG, config);

    // 保存光标可见状态
    si->cursor_visible = show;

    return B_OK;
}

// 设置亮度
static status_t
set_brightness(device_info *di, uint8 brightness)
{
    TRACE("set_brightness(): %d\n", brightness);

    // 龙芯7A2000 GPU不支持亮度调节
    // 这里可以通过调整Gamma值来模拟亮度调节

    return B_OK;
}

// 处理设备IOCTL命令
status_t
device_ioctl(device_info *di, uint32 cmd, void *buf, size_t len)
{
    ls7a2000_shared_info *si = di->si;
    status_t status = B_OK;

    TRACE("device_ioctl(): cmd=0x%lx, buf=%p, len=%lu\n", cmd, buf, len);

    // 检查参数
    if (di == NULL || si == NULL)
    {
        ERROR("Invalid device info\n");
        return B_BAD_VALUE;
    }

    // 处理命令
    switch (cmd)
    {
    case LS7A2000_GET_DEVICE_INFO:
        // 获取设备信息
        if (buf != NULL && len >= sizeof(uint32))
        {
            *(uint32 *)buf = si->magic;
            status = B_OK;
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_GET_EDID:
        // 获取EDID数据
        if (buf != NULL && len >= 128)
        {
            memcpy(buf, si->edid, 128);
            status = B_OK;
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_SET_DISPLAY_MODE:
        // 设置显示模式
        if (buf != NULL && len >= sizeof(ls7a2000_display_mode))
        {
            status = set_display_mode(di, (ls7a2000_display_mode *)buf);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_GET_DISPLAY_MODE:
        // 获取当前显示模式
        if (buf != NULL && len >= sizeof(ls7a2000_display_mode))
        {
            ls7a2000_display_mode *mode = (ls7a2000_display_mode *)buf;
            mode->width = si->width;
            mode->height = si->height;
            mode->bits_per_pixel = si->bits_per_pixel;
            mode->color_space = si->color_space;
            mode->flags = si->current_mode;
            mode->refresh_rate = 60; // 固定为60Hz
            status = B_OK;
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_GET_FRAME_BUFFER_CONFIG:
        // 获取帧缓冲配置
        if (buf != NULL && len >= sizeof(ls7a2000_frame_buffer_config))
        {
            ls7a2000_frame_buffer_config *config = (ls7a2000_frame_buffer_config *)buf;
            config->frame_buffer = si->frame_buffer;
            config->physical_frame_buffer = si->physical_frame_buffer;
            config->frame_buffer_size = si->frame_buffer_size;
            config->bytes_per_row = si->bytes_per_row;
            config->width = si->width;
            config->height = si->height;
            config->bits_per_pixel = si->bits_per_pixel;
            config->color_space = si->color_space;
            status = B_OK;
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_SET_DPMS_MODE:
        // 设置DPMS模式
        if (buf != NULL && len >= sizeof(uint32))
        {
            status = set_dpms_mode(di, *(uint32 *)buf);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_GET_DPMS_MODE:
        // 获取DPMS模式
        if (buf != NULL && len >= sizeof(uint32))
        {
            *(uint32 *)buf = si->dpms_mode;
            status = B_OK;
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_SET_BRIGHTNESS:
        // 设置亮度
        if (buf != NULL && len >= sizeof(uint8))
        {
            status = set_brightness(di, *(uint8 *)buf);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_SET_CURSOR_SHAPE:
        // 设置光标形状
        if (buf != NULL && len >= sizeof(ls7a2000_cursor_shape))
        {
            status = set_cursor_shape(di, (ls7a2000_cursor_shape *)buf);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_SET_CURSOR_POSITION:
        // 设置光标位置
        if (buf != NULL && len >= sizeof(ls7a2000_cursor_position))
        {
            status = set_cursor_position(di, (ls7a2000_cursor_position *)buf);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case LS7A2000_SHOW_CURSOR:
        // 显示光标
        if (buf != NULL && len >= sizeof(bool))
        {
            status = show_cursor(di, *(bool *)buf);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case B_GET_ACCELERANT_SIGNATURE:
        // 获取加速器签名
        if (buf != NULL && len >= sizeof("ls7a2000.accelerant"))
        {
            strcpy((char *)buf, "ls7a2000.accelerant");
            status = B_OK;
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case B_GET_CURRENT_MODE:
        // 获取当前模式（兼容B_ACCELERANT_DEVICE_HOOK）
        if (buf != NULL && len >= sizeof(display_mode))
        {
            display_mode *mode = (display_mode *)buf;
            mode->virtual_width = si->width;
            mode->virtual_height = si->height;
            mode->space = si->color_space;
            status = B_OK;
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case B_SET_CURSOR_SHAPE:
        // 设置光标形状（兼容B_ACCELERANT_DEVICE_HOOK）
        if (buf != NULL && len >= sizeof(cursor_info))
        {
            cursor_info *info = (cursor_info *)buf;
            ls7a2000_cursor_shape shape;
            shape.width = info->width;
            shape.height = info->height;
            shape.hot_x = info->hot_x;
            shape.hot_y = info->hot_y;
            shape.data = info->data;
            status = set_cursor_shape(di, &shape);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case B_MOVE_CURSOR:
        // 移动光标（兼容B_ACCELERANT_DEVICE_HOOK）
        if (buf != NULL && len >= sizeof(uint16) * 2)
        {
            uint16 *pos = (uint16 *)buf;
            ls7a2000_cursor_position cursor_pos;
            cursor_pos.x = pos[0];
            cursor_pos.y = pos[1];
            status = set_cursor_position(di, &cursor_pos);
        }
        else
        {
            status = B_BAD_VALUE;
        }
        break;

    case B_SHOW_CURSOR:
        // 显示光标（兼容B_ACCELERANT_DEVICE_HOOK）
        status = show_cursor(di, true);
        break;

    case B_HIDE_CURSOR:
        // 隐藏光标（兼容B_ACCELERANT_DEVICE_HOOK）
        status = show_cursor(di, false);
        break;

    default:
        // 未知命令
        ERROR("Unknown ioctl: 0x%lx\n", cmd);
        status = B_DEV_INVALID_IOCTL;
        break;
    }

    return status;
}